<template>
  <div class="chat-section">
    <vue-advanced-chat
      class="chat-panel"
      show-audio="false"
      show-add-room="false"
      accepted-files="image/png, image/jpeg"
      :current-user-id="senderId"
      :rooms="JSON.stringify(rooms)"
      :room-id="roomId"
      :rooms-loaded="true"
      :messages="JSON.stringify(messages)"
      :messages-loaded="messagesLoaded"
      @send-message="sendMessage($event.detail[0])"
      @fetch-messages="fetchMessages($event.detail[0])"
      @open-file="openFile($event.detail[0])"
    />
  </div>
</template>

<script>
import Vue from "vue";
import { ACCESS_TOKEN } from "@/store/mutation-types";
import { encrypt, decrypt } from "@/utils/encryption/crypto-util";
import createWebSocket from "@/utils/WebSocketClient";
import { mapState } from "vuex";
import { register } from "vue-advanced-chat";

const PING_INTERVAL = 10 * 1000;
const RECONNECT_INTERVAL = 0.5 * 1000;

register();

export default {
  data() {
    return {
      senderId: null,
      roomId: null,
      selectedRoom: null,
      selectedConversation: null,
      rooms: [],
      messages: [],
      messagesLoaded: false,

      ws: null,
    };
  },

  computed: {
    ...mapState("user", {
      userInfo: (state) => state.info,
    }),

    ...mapState("iMConversation", {
      conversationList: (state) => state.list,
    }),

    ...mapState("iMMessage", {
      items: (state) => state.items,
      pageSize: (state) => state.pageSize,
      pageTotal: (state) => state.pageTotal,
      totalCount: (state) => state.totalCount,
      currentPage: (state) => state.currentPage,
      originalPage: (state) => state.originalPage,
    }),
  },

  created() {
    // console.log('\n\n this.$store.getters.userId;:' + JSON.stringify(this.$store.getters.userId) + '\n\n');
    let token = Vue.ls.get(ACCESS_TOKEN);
    if (token) {
      //获取登录用户信息
      const that = this;
      this.$store
        .dispatch("user/getUserInfoAction")
        .then(() => {
          that.senderId = that.userInfo.id;
        })
        .then(() => {
          that.connectWebSocket();
        })
        .then(() => {
          this.getConversationList();
        })
        .catch(() => {
          Vue.ls.remove(ACCESS_TOKEN);
          that.$router.push({
            path: "/",
          });
        });
    } else {
      this.$router.push({
        path: "/",
      });
    }
  },

  destroyed: function () {
    // 离开页面生命周期函数
    if (this.ws) {
      this.ws.close();
    }
  },

  methods: {
    //TODO 获取当前登录用户的会话
    getConversationList() {
      const that = this;
      return new Promise((resolve, reject) => {
        that.$store
          .dispatch("iMConversation/getListAction")
          .then(() => {
            that.rooms = [];
            that.conversationList.forEach((conversation) => {
              that.rooms.push({
                roomId: conversation.id,
                roomName: conversation.ownerNickname,
                avatar: conversation.ownerAvatar,
                unreadCount: 0,
                users: [
                  { _id: conversation.ownerId, username: conversation.ownerNickname },
                  { _id: conversation.targetId, username: conversation.targetNickname },
                ],
              });
            });
            resolve();
          })
          .catch(() => reject());
      });
    },

    getSocketUrl() {
      const url = window._CONFIG["domianURL"].replace("https://", "wss://").replace("http://", "ws://") + "/websocket/" + this.senderId;
      return url.replace("api//", "api/");
    },

    getWsOptions() {
      var token = Vue.ls.get(ACCESS_TOKEN);
      return [token];
    },

    connectWebSocket() {
      const url = this.getSocketUrl();
      const wsOptions = this.getWsOptions();
      this.ws = createWebSocket(url, {
        wsOptions: wsOptions,
        autoReconnect: true,
        pingInterval: PING_INTERVAL,
        reconnectInterval: RECONNECT_INTERVAL,
      });

      this.ws.on("open", (event) => {
        console.log("WebSocket opened", event);
      });

      this.ws.on("message", (event) => {
        // console.log("WebSocket message received:", event.data);
        var data = eval("(" + event.data + ")"); //解析对象
        if (data.cmd == "chat") {
          this.receiveMessage(data);
        }
      });

      this.ws.on("close", (event) => {
        console.log("WebSocket closed", event);
      });

      this.ws.on("error", (event) => {
        console.error("WebSocket error:", event);
      });
    },

    resetMessages() {
      this.messagesLoaded = false;
      this.messages = [];
      this.$store.commit("iMMessage/RESET_CURRENT_PAGE");
      this.$store.commit("iMMessage/RESET_ITEMS");
    },

    getConversation(conversationId) {
      const conversations = this.conversationList.filter((conversation) => conversation.id === conversationId);

      if (Array.isArray(conversations) && conversations.length > 0) {
        return conversations[0];
      } else {
        return null;
      }
    },

    findFirstRoom(roomId) {
      const room = this.rooms.filter((room) => room.roomId === roomId);

      if (Array.isArray(room) && room.length > 0) {
        return room[0];
      } else {
        return null;
      }
    },

    fetchMessages({ room, options = {} }) {
      console.log("\n\n" + "fetchMessages" + "\n\n");
      this.selectedRoom = room.roomId;
      this.roomId = room.roomId;
      if (options.reset) {
        console.log("\n\n" + "切换会话 reset" + "\n\n");
        this.resetMessages();
      }

      if (room) room.unreadCount = 0;

      const conversation = this.getConversation(room.roomId);
      if (!this.selectedConversation || this.selectedConversation.id != room.roomId) {
        console.log("\n\n" + "切换会话" + "\n\n");

        this.selectedConversation = conversation;
        const isRefresh = false;
        this.updateList(conversation, isRefresh);
      } else {
        console.log("\n\n" + "同一会话" + "\n\n");
        const isRefresh = this.items.length < this.totalCount ? false : true;
        this.updateList(conversation, isRefresh);
      }
    },

    async sendMessage({ content, roomId, files, replyMessage }) {
      const room = this.findFirstRoom(roomId);
      if (room) room.unreadCount = 0;

      const conversations = this.conversationList.filter((conversation) => conversation.id === roomId);
      if (Array.isArray(conversations) && conversations.length > 0) {
        const conversation = conversations[0];
        await this.sendConversationMessage(conversation.targetId, conversation.ownerId, conversation, { content, roomId, files, replyMessage });
      }
    },

    formattedFiles(files) {
      const formattedFiles = [];

      files.forEach((file) => {
        const messageFile = {
          name: file.name,
          size: file.size,
          type: file.type,
          extension: file.extension || file.type,
          url: file.url || file.localUrl,
        };

        if (file.audio) {
          messageFile.audio = true;
          messageFile.duration = file.duration;
        }

        formattedFiles.push(messageFile);
      });

      return formattedFiles;
    },

    openFile({ file }) {
      window.open(file.file.url, "_blank");
    },

    async sendConversationMessage(senderId, receiverId, conversation, { content, roomId, files, replyMessage }) {
      const id = this.messages.length;
      // 0 - 文本 1 - 图片
      var type = 0;
      const message = {
        _id: id,
        senderId: senderId,
        content,
        timestamp: new Date().toString().substring(16, 21),
        date: new Date().toDateString(),
      };

      // 本地聊天会话先添加消息
      this.messages = [...this.messages, message];

      if (files) {
        message.files = this.formattedFiles(files);
        for (let index = 0; index < files.length; index++) {
          await this.uploadFile({ file: files[index], message: message });
        }
      }

      var c = message.content;
      if (files) {
        type = 1;
        c = JSON.stringify(files);
      }

      // 发送消息到服务器
      const key = conversation.cryptoKey;
      const iv = conversation.cryptoIv;
      const body = {
        cmd: "chat",
        type: type,
        conversationId: conversation.id,
        receiverId: receiverId,
        senderId: senderId,
        content: encrypt(c, key, iv),
        timestamp: new Date().getTime(),
      };

      this.ws.send(JSON.stringify(body));
    },

    async uploadFile({ file, message }) {
      const that = this;
      return new Promise((resolve, reject) => {
        let type = file.extension || file.type;
        if (type === "svg" || type === "pdf") {
          type = file.type;
        }

        const onUploadProgress = (progressEvent) => {
          const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
          that.updateFileProgress(message._id, file.localUrl, progress);
        };

        const blob = file.blob;
        const name = file.name + "." + file.extension;

        let formData = new FormData();
        formData.append("file", blob, name);
        const parameters = { formData, onUploadProgress };

        this.$store
          .dispatch("upload/commonAction", parameters)
          .then((url) => {
            that.$message.success("上传成功");
            message.files.forEach((f) => {
              if (f.url === file.localUrl) {
                f.url = that.$fileBaseURL + url;
                f.progress = -1;
              }
            });

            //更新file的url为服务器相对路径 用于服务器保存
            file.url = url;
            this.messages = [...this.messages];
            resolve();
          })
          .catch(() => {
            that.$message.error("上传失败");
            reject();
          })
          .finally(() => {
            that.resetUpload();
          });
      });
    },

    updateFileProgress(messageId, fileUrl, progress) {
      const message = this.messages.find((message) => message._id === messageId);

      if (!message || !message.files) return;

      message.files.find((file) => file.url === fileUrl).progress = progress;
      this.messages = [...this.messages];
    },

    resetUpload() {},

    parseMessage(message, conversation) {
      const key = conversation.cryptoKey;
      const iv = conversation.cryptoIv;
      const type = message.type;
      var content = message.content;
      if (type == 1 && content && content.length > 0) {
        const filesContent = decrypt(content, key, iv);
        // console.log("\n\n filesContent:" + JSON.stringify(filesContent) + "\n\n");
        message.files = JSON.parse(filesContent);
        message.files.forEach((f) => {
          f.url = this.$fileBaseURL + f.url;
        });
        message.content = "";
      } else {
        message.content = decrypt(content, key, iv);
      }

      message._id = message.id ? message.id : this.messages.length;
      message.timestamp = new Date(Number(message.timestamp)).toLocaleString();

      return message;
    },

    updateList(conversation, isRefresh) {
      const that = this;
      const parameters = {
        pageSize: this.pageSize,
        pageNo: this.currentPage,
        conversationId: conversation.id,
      };

      const autoIncrementPage = true;
      this.$store
        .dispatch("iMMessage/getListAction", { isRefresh, parameters, autoIncrementPage })
        .then(() => {
          const messages = JSON.parse(JSON.stringify(that.messages));

          for (var i = that.messages.length; i < that.items.length; i++) {
            const parsedMessage = that.parseMessage(that.items[i], conversation);
            messages.unshift(parsedMessage);
          }

          that.messages = messages;

          if (messages.length === 0 || messages.length < this.pageSize || this.messages.length >= this.totalCount) {
            setTimeout(() => {
              this.messagesLoaded = true;
            }, 0);
          } else {
            this.messagesLoaded = false;
          }
        })
        .catch((error) => {
          that.$message.error(error.message);
        });
    },

    receiveMessage(message) {
      // Conversation 为空
      if (this.selectedConversation) {
        //非选中会话有消息
        if (this.selectedConversation.id != message.conversationId) {
          console.log("\n\n" + "消息为非选中会话" + "\n\n");
          this.updateRoomUnreadCount(message.conversationId);
        } else {
          this.updateMessages(message, this.selectedConversation);
        }
      } else {
        //一开始没有任何会话接受到消息的时候需要创建新的会话
        this.updateConversation(message);
      }
    },

    updateRoomUnreadCount(conversationId) {
      const room = this.findFirstRoom(conversationId);
      console.log("\n\n 接受消息的非选中 room:" + JSON.stringify(room) + "\n\n");
      if (room) {
        room.unreadCount++;
        console.log("\n\n 更新 room unreadCount" + JSON.stringify(room) + "\n\n");
      } else {
        this.getConversationList()
          .then(() => {
            const room = this.findFirstRoom(conversationId);
            if (room) {
              room.unreadCount++;
              console.log("\n\n 更新 room unreadCount" + JSON.stringify(room) + "\n\n");
            }
          })
          .catch(() => {});
      }
    },

    updateConversation(message) {
      const that = this;
      const conversation = this.getConversation(message.conversationId);
      console.log("\n\n getConversation:" + conversation + "\n\n");
      if (conversation) {
        this.selectedConversation = conversation;
        that.updateMessages(message, conversation);
      } else {
        this.getConversationList().then(() => {
          const conversation = that.getConversation(message.conversationId);
          console.log("\n\ngetConversationList  :" + conversation + "\n\n");
          if (conversation) {
            that.selectedConversation = conversation;
            that.updateMessages(message, conversation);
          } else {
            // Vue.ls.remove(ACCESS_TOKEN)
          }
        });
      }
    },

    updateMessages(message, conversation) {
      this.messages = [...this.messages, this.parseMessage(message, conversation)];
    },
  },
};
</script>

<style lang="less">
.chat-section {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.chat-panel {
  min-width: 800px;
}
</style>
